{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Autograd experiments\n",
    "\n",
    "**References**\n",
    "* https://github.com/HIPS/autograd/blob/master/docs/tutorial.md\n",
    "* https://github.com/HIPS/autograd/blob/master/docs/updateguide.md\n",
    "\n",
    "abbreviations:\n",
    "* ax= axis\n",
    "* dwrt= derivative with respect to"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example of @primitive usage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import autograd.numpy as anp\n",
    "from autograd import grad, jacobian\n",
    "from autograd.extend import primitive, defvjp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fun(x, y, z):\n",
    "    \"\"\"\n",
    "    x : float\n",
    "    y: float\n",
    "    z : bool\n",
    "    \n",
    "    \"\"\"\n",
    "    return x * y**2 if z else y**2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def d_fun(x, y, z, dwrt):\n",
    "    if z:\n",
    "        if dwrt == 0:\n",
    "            return y**2\n",
    "        elif dwrt == 1:\n",
    "            return 2*x*y\n",
    "        else:\n",
    "            assert False, 'z not differentiable'\n",
    "    else:\n",
    "        if dwrt == 0:\n",
    "            return 0.\n",
    "        elif dwrt == 1:\n",
    "            return 2*y\n",
    "        else:\n",
    "            assert False, 'z not differentiable'  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "fun_x = grad(fun, 0)\n",
    "fun_y = grad(fun, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0\n",
      "0.0\n",
      "0.0\n",
      "0.0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/rrtucci/anaconda3/envs/qubiter/lib/python3.7/site-packages/autograd/tracer.py:14: UserWarning: Output seems independent of input.\n",
      "  warnings.warn(\"Output seems independent of input.\")\n"
     ]
    }
   ],
   "source": [
    "for z in [True,False]:\n",
    "    print(fun_x(.5, .1, z) - d_fun(.5, .1, z, dwrt=0))\n",
    "    print(fun_y(.5, .1, z) - d_fun(.5, .1, z, dwrt=1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "@primitive\n",
    "def pfun(x, y, z):\n",
    "    return fun(x, y, z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "defvjp(pfun,\n",
    "       lambda ans, x, y, z: lambda g: g*d_fun(x, y, z, 0),  # g and pfun scalars\n",
    "       lambda ans, x, y, z: lambda g: g*d_fun(x, y, z, 1),\n",
    "       argnums=[0, 1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "pfun_x = grad(pfun, 0)\n",
    "pfun_y = grad(pfun, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0\n",
      "0.0\n",
      "0.0\n",
      "0.0\n"
     ]
    }
   ],
   "source": [
    "for z in [True,False]:\n",
    "    print(pfun_x(.5, .1, z) - d_fun(.5, .1, z, 0))\n",
    "    print(pfun_y(.5, .1, z) - d_fun(.5, .1, z, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Storing Pauli matrices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 1],\n",
       "       [1, 0]])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sigx = anp.array([[0, 1],[1,0]])\n",
    "sigx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.+0.j, -0.-1.j],\n",
       "       [ 0.+1.j,  0.+0.j]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sigy = anp.array([[0, -1j],[1j,0]])\n",
    "sigy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1,  0],\n",
       "       [ 0, -1]])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sigz = anp.array([[1, 0],[0, -1]])\n",
    "sigz"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.+0.j,  1.+0.j],\n",
       "       [ 1.+0.j,  0.+0.j],\n",
       "       [ 0.+0.j, -0.-1.j],\n",
       "       [ 0.+1.j,  0.+0.j],\n",
       "       [ 1.+0.j,  0.+0.j],\n",
       "       [ 0.+0.j, -1.+0.j]])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sig_all = anp.vstack([sigx, sigy, sigz])\n",
    "sig_all"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[ 0.+0.j,  0.+0.j,  1.+0.j],\n",
       "        [ 1.+0.j, -0.-1.j,  0.+0.j]],\n",
       "\n",
       "       [[ 1.+0.j,  0.+1.j,  0.+0.j],\n",
       "        [ 0.+0.j,  0.+0.j, -1.+0.j]]])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sig_all = anp.reshape(sig_all, (3, 2, 2)).transpose(1, 2, 0)\n",
    "sig_all"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sigx_=\n",
      " [[0.+0.j 1.+0.j]\n",
      " [1.+0.j 0.+0.j]]\n",
      "sigy_=\n",
      " [[0.+0.j 0.-1.j]\n",
      " [0.+1.j 0.+0.j]]\n",
      "sigz_=\n",
      " [[ 1.+0.j  0.+0.j]\n",
      " [ 0.+0.j -1.+0.j]]\n"
     ]
    }
   ],
   "source": [
    "ex = anp.array([1,0,0])\n",
    "ey = anp.array([0,1,0])\n",
    "ez = anp.array([0,0,1])\n",
    "sigx_ = anp.dot(sig_all, ex)\n",
    "sigy_ = anp.dot(sig_all, ey)\n",
    "sigz_ = anp.dot(sig_all, ez)\n",
    "print('sigx_=\\n', sigx_)\n",
    "print('sigy_=\\n', sigy_)\n",
    "print('sigz_=\\n', sigz_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## rot1, single parameter rotation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$ U = e^{i\\sigma_3\\theta_3} = C + i\\sigma_3 S$\n",
    "\n",
    "$S = \\sin\\theta_3, C = \\cos \\theta_3$\n",
    "\n",
    "$\\frac{dU}{dt} = \\dot{\\theta}_3(-S + i\\sigma_3 C)$\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rot1(t, ax):\n",
    "    assert ax in [1, 2, 3]\n",
    "    return anp.eye(2)*anp.cos(t) + 1j*sig_all[:, :, ax-1]*anp.sin(t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rot1_t(t, ax):\n",
    "    assert  ax in [1, 2, 3]\n",
    "    return -anp.eye(2)*anp.sin(t) + 1j*sig_all[:, :, ax-1]*anp.cos(t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def d_auto_rot1(t, ax):\n",
    "    assert  ax in [1, 2, 3]\n",
    "    def rot1r(t, ax):\n",
    "        return anp.real(rot1(t, ax))\n",
    "    def rot1i(t, ax):\n",
    "        return anp.imag(rot1(t, ax))\n",
    "    return jacobian(rot1r, 0)(t, ax) + 1j*jacobian(rot1i, 0)(t, ax) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0.+0.j 0.+0.j]\n",
      " [0.+0.j 0.+0.j]]\n",
      "[[0.+0.j 0.+0.j]\n",
      " [0.+0.j 0.+0.j]]\n",
      "[[0.+0.j 0.+0.j]\n",
      " [0.+0.j 0.+0.j]]\n"
     ]
    }
   ],
   "source": [
    "for ax in range(1, 4):\n",
    "    print(d_auto_rot1(.5, 1) - rot1_t(.5, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## arbitrary 2-dim unitary (4 parameters)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$ U = e^{i\\sigma_k\\theta_k} = C +\n",
    "i\\sigma_k \\frac{\\theta_k}{\\theta} S$\n",
    "\n",
    "$\\theta = \\sqrt{\\theta_k\\theta_k},\n",
    "S = \\sin\\theta, C = \\cos \\theta$\n",
    "\n",
    "$\\frac{dU}{dt}=-S \\frac{\\theta_k}{\\theta}\n",
    "\\dot{\\theta_k}+ i\\sigma_k\\dot{\\theta_r}\n",
    "\\left[\\frac{\\theta_k\\theta_r}{\\theta^2} C+\n",
    "\\frac{S}{\\theta}(-\\frac{\\theta_k\\theta_r}{\\theta^2}+\\delta_{k, r})\\right]$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "def u2(*tlist):\n",
    "    assert len(tlist) == 4\n",
    "    t = anp.sqrt(tlist[1]**2 + tlist[2]**2 + tlist[3]**2)\n",
    "    tvec = anp.array([tlist[1]/t, tlist[2]/t, tlist[3]/t])\n",
    "    out = anp.eye(2)*anp.cos(t) + 1j*anp.dot(sig_all, tvec)*anp.sin(t) \n",
    "    return anp.exp(1j*tlist[0])*out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def d_u2(dwrt, *tlist):\n",
    "    assert dwrt in range(4)\n",
    "    assert len(tlist) == 4\n",
    "    if dwrt == 0:\n",
    "        return 1j*u2(*tlist)\n",
    "    dwrt -= 1\n",
    "    t = anp.sqrt(tlist[1]**2 + tlist[2]**2 + tlist[3]**2)\n",
    "    tvec = anp.array([tlist[1]/t, tlist[2]/t, tlist[3]/t])\n",
    "    dotted_vec = tvec*tvec[dwrt]*anp.cos(t) + (anp.sin(t)/t)*(-tvec*tvec[dwrt] + anp.eye(3)[dwrt, :])\n",
    "    out = -anp.sin(t)*tvec[dwrt]*anp.eye(2) + 1j*anp.dot(sig_all, dotted_vec)\n",
    "    return anp.exp(1j*tlist[0])*out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.81615064+0.46474597j,  0.26526593+0.21804425j],\n",
       "       [-0.30329697+0.16099768j,  0.89221274-0.29333789j]])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "u2(.1, .2, .3, .4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.+0.j, 0.+0.j],\n",
       "       [0.+0.j, 0.+0.j]])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "u2(0., 0., 0., .4) - rot1(.4, ax=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "def d_auto_u2(dwrt, *tlist):\n",
    "    def u2r(*tlist):\n",
    "        return anp.real(u2(*tlist))\n",
    "    def u2i(*tlist):\n",
    "        return anp.imag(u2(*tlist))\n",
    "    return jacobian(u2r, dwrt)(*tlist) + 1j*jacobian(u2i, dwrt)(*tlist) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[-0.47942554+0.87758256j  0.        +0.j        ]\n",
      " [ 0.        +0.j          0.47942554+0.87758256j]]\n",
      "[[0.+0.j         0.+0.95885108j]\n",
      " [0.+0.95885108j 0.+0.j        ]]\n",
      "[[ 0.        +0.j  0.95885108+0.j]\n",
      " [-0.95885108+0.j  0.        +0.j]]\n",
      "[[-0.47942554+0.87758256j  0.        +0.j        ]\n",
      " [ 0.        +0.j         -0.47942554-0.87758256j]]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/rrtucci/anaconda3/envs/qubiter/lib/python3.7/site-packages/numpy/core/_asarray.py:85: ComplexWarning: Casting complex values to real discards the imaginary part\n",
      "  return array(a, dtype, copy=False, order=order)\n"
     ]
    }
   ],
   "source": [
    "for dwrt in range(4):\n",
    "    print(d_auto_u2(dwrt, 0., 0., 0., .5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.47942554+0.87758256j,  0.        +0.j        ],\n",
       "       [ 0.        +0.j        , -0.47942554-0.87758256j]])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rot1_t(.5, ax=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0\n",
      "2.5814177514652363e-16\n",
      "1.4833660915169986e-16\n",
      "2.786810452962585e-16\n"
     ]
    }
   ],
   "source": [
    "tlist = [.3, 1.1, .7, .5]\n",
    "for dwrt in range(4):\n",
    "    print(anp.linalg.norm(d_auto_u2(dwrt, *tlist) - d_u2(dwrt, *tlist)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## u2 as primitive\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "@primitive\n",
    "def pu2r(*tlist):\n",
    "    return anp.real(u2(*tlist))\n",
    "@primitive\n",
    "def pu2i(*tlist):\n",
    "    return anp.imag(u2(*tlist))\n",
    "def pu2(*tlist):\n",
    "    return pu2r(*tlist) + 1j*pu2i(*tlist)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "defvjp(pu2r,\n",
    "       lambda ans, *tlist: lambda g: anp.sum(g*anp.real(d_u2(0, *tlist))), # g.shape==pu2r.shape\n",
    "       lambda ans, *tlist: lambda g: anp.sum(g*anp.real(d_u2(1, *tlist))),\n",
    "       lambda ans, *tlist: lambda g: anp.sum(g*anp.real(d_u2(2, *tlist))),\n",
    "       lambda ans, *tlist: lambda g: anp.sum(g*anp.real(d_u2(3, *tlist))),\n",
    "       argnums=range(4))\n",
    "defvjp(pu2i,\n",
    "       lambda ans, *tlist: lambda g: anp.sum(g*anp.imag(d_u2(0, *tlist))), # g.shape==pu2i.shape\n",
    "       lambda ans, *tlist: lambda g: anp.sum(g*anp.imag(d_u2(1, *tlist))),\n",
    "       lambda ans, *tlist: lambda g: anp.sum(g*anp.imag(d_u2(2, *tlist))),\n",
    "       lambda ans, *tlist: lambda g: anp.sum(g*anp.imag(d_u2(3, *tlist))),\n",
    "       argnums=range(4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "def d_auto_pu2(dwrt, *tlist):\n",
    "    assert dwrt in range(4)\n",
    "    return jacobian(pu2r, dwrt)(*tlist) + 1j*jacobian(pu2i, dwrt)(*tlist)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0\n",
      "0.0\n",
      "0.0\n",
      "0.0\n"
     ]
    }
   ],
   "source": [
    "tlist = [.1, .2, .3, .4]\n",
    "for dwrt in range(4):\n",
    "    print(anp.linalg.norm(d_auto_pu2(dwrt, *tlist) - d_u2(dwrt, *tlist)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  },
  "toc": {
   "colors": {
    "hover_highlight": "#DAA520",
    "running_highlight": "#FF0000",
    "selected_highlight": "#FFD700"
   },
   "moveMenuLeft": true,
   "nav_menu": {
    "height": "12px",
    "width": "252px"
   },
   "navigate_menu": true,
   "number_sections": true,
   "sideBar": true,
   "threshold": 4,
   "toc_cell": false,
   "toc_section_display": "block",
   "toc_window_display": false,
   "widenNotebook": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
